#include<iostream>
#include<string.h>
#include<unistd.h>
#include<unordered_map>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include"log.hpp"
using namespace std;

class udpserver
{
public:
    udpserver(int port,string ip=""):port_(port),ip_(ip),socketfd_(-1)
    {

    }
    ~udpserver()
    {

    }
    void init()
    {
        //1创建socket套接字
        //本地通信还是跨网络通信，数据类型（数据流还是数据报），协议类型:网络应用中设为0
        //返回值是文件描述符
        socketfd_ = socket(AF_INET,SOCK_DGRAM,0);
        if (socketfd_ < 0)
        {
            LogMessage(FATAL,"socket:%s,%d",strerror(errno),socketfd_);
            exit(1);
        }
        else
        {
            LogMessage(DEBUG,"socket create success:%d",socketfd_);
        }
        //2绑定网络信息
        //2.1填充网络信息
        struct sockaddr_in local;
        bzero(&local,sizeof(local));//讲local的每一个字节都置为0
        //填充协议家族，域（就是前16个字节）
        local.sin_family=AF_INET;
        //填充端口号
        local.sin_port=htons(port_);//字节序转换：主机转网络
        //填充ip
        //INADDR_ANY地址为0，填充任意一个ip地址
        //IP地址的作用：可以在网络当中唯一标识一台主机，一个公网IP地址只能被一台机器所占有，一个机器可以拥有多个IP地址。
        //填充ip地址需要填充四字节的格式，inet_addr是将一个指定的ip由点分十进制转换到四字节，同时也会自动转换字节序
        //一台服务器可能有一个ip地址，也可能有多个ip地址，INADDR_ANY，会绑定这台主机的所有ip地址
        local.sin_addr.s_addr=ip_.empty()?htons(INADDR_ANY):inet_addr(ip_.c_str());
        //2.2绑定网络信息
        if(bind(socketfd_,(const sockaddr*)&local,sizeof(local))==-1)
        {
            LogMessage(FATAL,"bind:%s:%d",strerror(errno),socketfd_);
            exit(2);
        }
        else
        {
            LogMessage(DEBUG,"bind sucess:%d",socketfd_);
        }
    }

    void CheckUser(string userip,uint16_t userport,struct sockaddr_in &peer)
    {
        string key=userip;
        key+=":";
        key+=to_string(userport);
        if(users.find(key)==users.end())
        {
            users.insert({key,peer});
        }
    }

    void MessageRoutine(string ip,uint16_t port,string info)
    {
        string message="[";
        message+=ip;
        message+=":";
        message+=to_string(port);
        message+="]:";
        message+=info;
        //将收到的数据发送给所有客户端
        for(auto& i : users)
        {
            sendto(socketfd_,message.c_str(),message.size(),0,(const sockaddr*)&i.second,sizeof(i.second));
        }
    }

    void start()
    {
        char inbuffer[1024];    //将来读取到的数据放在这里
        char outbuffer[1024];   //这是要发送的数据
        while(true)
        {
            //LogMessage(NOTICE," server提供服务中");
            //将读取到的数据放到inbuffer
            //peer是通过网络发过来的客户端信息（发送方的网络信息），是网络字节序，后续使用需要转换成主机字节序
            struct sockaddr_in peer;      // 一个结构体，存放的是客户端的信息，因为要回信息，需要知道是谁发的,输出型参数
            socklen_t len = sizeof(peer); // peer的长度，输入输出型参数
            // 接受数据
            size_t s = recvfrom(socketfd_,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&peer,&len);
            if(s>0)
            {
                inbuffer[s]=0;
            }
            else if(s==-1)
            {
                LogMessage(FATAL,"recvfrom:%s:%d",strerror(errno),socketfd_);
                continue;
            }
            //接收成功,获取客户端的ip和port
            string peerip = inet_ntoa(peer.sin_addr);
            uint16_t peer_port=ntohs(peer.sin_port);
            //加入users
            CheckUser(peerip,peer_port,peer);
            //打印客户端发送来的数据
            LogMessage(NOTICE,"[%s:%d]:%s",peerip.c_str(),peer_port,inbuffer);
            //消息路由:就是将客户端收到的所有数据发给每一个客户端
            MessageRoutine(peerip,peer_port,inbuffer);

        }
    }


private:
    int socketfd_;//服务器的socketfd
    uint16_t port_;//服务器的端口号
    string ip_;//服务器的ip
    unordered_map<string,struct sockaddr_in> users;//online users
};

int main(int argc, char *argv[])
{
    cout<<"pid:"<<getpid()<<endl;
    if(argc!=2&&argc!=3)
    {
        cout<<"failed"<<endl;
        exit(3);
    }
    uint16_t port=atoi(argv[1]);
    string ip;
    if(argc==3)
    {
        ip=argv[2];
    }
    udpserver svr(port,ip);
    svr.init();
    svr.start();
    return 0;


}